package springdao.reposotory;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.hibernate.Hibernate;
import org.hibernate.Query;
import org.hibernate.HibernateException;
import org.hibernate.LockOptions;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.collection.internal.AbstractPersistentCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.support.DaoSupport;
import org.springframework.orm.hibernate4.SessionFactoryUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import springdao.support.AliasHelper;
import springdao.DaoRepository;
import springdao.HasSessionFactory;
import springdao.model.Member;
import springdao.support.LockParser;

/**
 *
 * @author Kent Yeh
 */
public class AnnotherDaoRepository extends DaoSupport implements HasSessionFactory, DaoRepository<Member> {

    static Logger logger = LoggerFactory.getLogger(AnnotherDaoRepository.class);

    public Class<Member> getClazz() {
        return Member.class;
    }
    private SessionFactory sessionFactory;

    @Override
    protected void checkDaoConfig() throws IllegalArgumentException {
        if (this.sessionFactory == null) {
            throw new IllegalArgumentException("'sessionFactory' is required");
        }
    }

    @Override
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    @Override
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    private LockOptions getLockMode(String lockMode) {
        LockOptions mode = LockOptions.NONE;
        try {
            mode = LockParser.parse(lockMode);
        } catch (RuntimeException ex) {
            mode = LockOptions.NONE;
        }
        return mode;
    }

    @Override
    public boolean isJpql() {
        return false;
    }

    @Override
    public void setJpql(boolean jpql) {
        //
    }

    @Override
    public Member instanate() throws InstantiationException, IllegalAccessException {
        try {
            return getClazz().newInstance();
        } catch (InstantiationException ex) {
            logger.error("Instanate error", ex);
            throw ex;
        } catch (IllegalAccessException ex) {
            logger.error("Instanate error", ex);
            throw ex;
        }
    }

    @Override
    public void clear() {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.clear();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public boolean contains(Object entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return session.contains(entity);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member findByPrimaryKey(Serializable primaryKey) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return (Member) session.load(getClazz(), primaryKey);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member findByPrimaryKey(Serializable primaryKey, String lockMode) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {

            return (Member) session.load(getClazz(), primaryKey, getLockMode(lockMode));
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    /**
     * 
     * @param primaryKey
     * @param properties Not used.不使用
     * @return 
     */
    @Override
    public Member findByPrimaryKey(Serializable primaryKey, Map<String, Object> properties) {
        return findByPrimaryKey(primaryKey);
    }

    /**
     * 
     * @param primaryKey
     * @param lockMode
     * @param properties Not used.不使用
     * @return 
     */
    @Override
    public Member findByPrimaryKey(Serializable primaryKey, String lockMode, Map<String, Object> properties) {
        return findByPrimaryKey(primaryKey, lockMode);
    }

    @Override
    public Member save(Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.save(entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Collection<Member> save(Collection<Member> entities) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            for (Object entity : entities) {
                session.save(entity);
            }
            session.flush();
            return entities;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member persist(Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.persist(entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member persist(String entityName, Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.persist(entityName, entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member update(Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.update(entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member update(Member entity, String lockMode) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.update(entity);
            if (StringUtils.hasText(lockMode)) {
                LockOptions option = getLockMode(lockMode);
                if (LockOptions.NONE.equals(option)) {
                    session.buildLockRequest(option).lock(entity);
                }
            }
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Collection<Member> update(Collection<Member> entities) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            for (Object entity : entities) {
                session.update(entity);
            }
            session.flush();
            return entities;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member merge(Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.merge(entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member merge(String entityName, Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.merge(entityName, entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    public Collection<Member> merge(Collection<Member> entities) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            for (Object entity : entities) {
                session.merge(entity);
            }
            session.flush();
            return entities;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member saveOrUpdate(Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.saveOrUpdate(entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member saveOrUpdate(String entityName, Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.saveOrUpdate(entityName, entity);
            session.flush();
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Collection<Member> saveOrUpdate(Collection<Member> entities) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            for (Member entity : entities) {
                session.saveOrUpdate(entity);
            }
            return entities;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public void delete(Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.delete(entity);
            session.flush();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public void delete(Member entity, String lockMode) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            LockOptions option = getLockMode(lockMode);
            if (LockOptions.NONE.equals(option)) {
                session.buildLockRequest(option).lock(entity);
            }
            session.delete(entity);
            session.flush();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public void delete(Collection<Member> entities) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            for (Member entity : entities) {
                session.delete(entity);
            }
            session.flush();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member lock(Member entity, String lockMode) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            LockOptions option = getLockMode(lockMode);
            if (LockOptions.NONE.equals(option)) {
                session.buildLockRequest(option).lock(entity);
            }
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member lock(String entityName, Member entity, String lockMode) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            LockOptions option = getLockMode(lockMode);
            if (LockOptions.NONE.equals(option)) {
                session.buildLockRequest(option).lock(entityName, entity);
            }
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member refresh(Member entity) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.refresh(entity);
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member refresh(Member entity, String lockMode) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            session.refresh(entity, getLockMode(lockMode));
            return entity;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public int bulkUpdate(String QL) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return session.createQuery(QL).executeUpdate();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Integer> bulkUpdate(List<String> QLs) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            List<Integer> res = new ArrayList<Integer>();
            for (String queryString : QLs) {
                res.add(session.createQuery(queryString).executeUpdate());
            }
            return res;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public int bulkUpdate(String QL, Object... parameters) {
        if (parameters == null || parameters.length == 0) {
            return bulkUpdate(QL);
        } else {
            Session session = SessionFactoryUtils.openSession(sessionFactory);
            try {
                Query queryObject = session.createQuery(QL);
                if (parameters != null) {
                    for (int i = 0; i < parameters.length; i++) {
                        queryObject.setParameter(i, parameters[i]);
                    }
                }
                return queryObject.executeUpdate();
            } finally {
                SessionFactoryUtils.closeSession(session);
            }
        }
    }

    @Override
    public int bulkUpdate(String QL, Map<String, ?> parameters) {
        if (parameters == null || parameters.isEmpty()) {
            return bulkUpdate(QL);
        } else {
            Session session = SessionFactoryUtils.openSession(sessionFactory);
            try {
                Query queryObject = session.createQuery(QL);
                if (parameters != null && !parameters.isEmpty()) {
                    for (String key : parameters.keySet()) {
                        queryObject.setParameter(key, parameters.get(key));
                    }
                }
                return queryObject.executeUpdate();
            } finally {
                SessionFactoryUtils.closeSession(session);
            }
        }
    }

    @Override
    public String getEntityName() {
        return getClazz().getName();
    }

    @Override
    public String getAliasName() {
        return AliasHelper.getAlias(getClazz());
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria) {
        return findByCriteria(qlCriteria, 0, 0);
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria, Object... parameters) {
        StringBuilder sb = new StringBuilder("from ").append(getEntityName()).
                append(" as ").append(getAliasName()).append(" ").append(qlCriteria);
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return findList(session.createQuery(sb.toString()), parameters);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria, Map<String, ?> parameters) {
        StringBuilder sb = new StringBuilder("from ").append(getEntityName()).
                append(" as ").append(getAliasName()).append(" ").append(qlCriteria);
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return findList(session.createQuery(sb.toString()), parameters);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria) {
        return findByJoinCriteria(joins, qlCriteria, 0, 0);
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, Object... parameters) {
        StringBuilder sb = new StringBuilder("select distinct ").append(getAliasName()).
                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).append(" ").
                append(joins).append(" ").append(qlCriteria);
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return findList(session.createQuery(sb.toString()), parameters);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, Map<String, ?> parameters) {
        StringBuilder sb = new StringBuilder("select distinct ").append(getAliasName()).
                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).append(" ").
                append(joins).append(" ").append(qlCriteria);
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return findList(session.createQuery(sb.toString()), parameters);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria, int startPageNo, int pageSize, Object... parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.length == 0 ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters);
        } else if (parameters == null || parameters.length == 0) {
            return findByCriteria(qlCriteria, startPageNo, pageSize);
        } else {
            StringBuilder sb = new StringBuilder(" from ").append(getEntityName()).append(" as ").
                    append(getAliasName()).append(" ").append(qlCriteria);
            Session session = SessionFactoryUtils.openSession(sessionFactory);
            try {
                return findList(session.createQuery(sb.toString()).
                        setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize), parameters);
            } finally {
                SessionFactoryUtils.closeSession(session);
            }
        }
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria, int startPageNo, int pageSize, Map<String, ?> parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.isEmpty() ? findByCriteria(qlCriteria) : findByCriteria(qlCriteria, parameters);
        } else if (parameters == null || parameters.isEmpty()) {
            return findByCriteria(qlCriteria, startPageNo, pageSize);
        } else {
            StringBuilder sb = new StringBuilder(" from ").append(getEntityName()).append(" as ").
                    append(getAliasName()).append(" ").append(qlCriteria);
            Session session = SessionFactoryUtils.openSession(sessionFactory);
            try {
                return findList(session.createQuery(sb.toString()).
                        setFirstResult((startPageNo - 1) * pageSize).setMaxResults(pageSize), parameters);
            } finally {
                SessionFactoryUtils.closeSession(session);
            }
        }
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, int startPageNo, int pageSize, Object... parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.length == 0 ? findByJoinCriteria(joins, qlCriteria)
                    : findByJoinCriteria(joins, qlCriteria, parameters);
        } else {
            if (parameters == null || parameters.length == 0) {
                return findByCriteria(qlCriteria, startPageNo, pageSize);
            } else {
                StringBuilder sb = new StringBuilder("select ").append(getAliasName()).
                        append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).
                        append(" ").append(joins).append(" ").append(qlCriteria);
                Session session = SessionFactoryUtils.openSession(sessionFactory);
                try {
                    return findList(session.createQuery(sb.toString()).setFirstResult((startPageNo - 1)
                            * pageSize).setMaxResults(pageSize), parameters);
                } finally {
                    SessionFactoryUtils.closeSession(session);
                }
            }
        }
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, int startPageNo, int pageSize, Map<String, ?> parameters) {
        if ((startPageNo < 1) || (pageSize < 1)) {
            return parameters == null || parameters.isEmpty() ? findByJoinCriteria(joins, qlCriteria)
                    : findByJoinCriteria(joins, qlCriteria, parameters);
        } else {
            if (parameters == null || parameters.isEmpty()) {
                return findByCriteria(qlCriteria, startPageNo, pageSize);
            } else {
                StringBuilder sb = new StringBuilder("select ").append(getAliasName()).
                        append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).
                        append(" ").append(joins).append(" ").append(qlCriteria);
                Session session = SessionFactoryUtils.openSession(sessionFactory);
                try {
                    return findList(session.createQuery(sb.toString()).setFirstResult((startPageNo - 1)
                            * pageSize).setMaxResults(pageSize), parameters);
                } finally {
                    SessionFactoryUtils.closeSession(session);
                }
            }
        }
    }

    @Override
    public List<Member> findByCriteria(String qlCriteria, int startPageNo, int pageSize) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            if ((startPageNo < 1) || (pageSize < 1)) {
                StringBuilder sb = new StringBuilder("from ").append(getEntityName()).
                        append(" as ").append(getAliasName()).append(" ").append(qlCriteria);
                return session.createQuery(sb.toString()).list();
            } else {
                StringBuffer sb = new StringBuffer("from ").append(getEntityName()).append(" as ").
                        append(getAliasName()).append(" ").append(qlCriteria);
                return session.createQuery(sb.toString()).setFirstResult((startPageNo - 1)
                        * pageSize).setMaxResults(pageSize).list();
            }
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByJoinCriteria(String joins, String qlCriteria, int startPageNo, int pageSize) {
        StringBuilder sb = new StringBuilder("select distinct ").append(getAliasName()).
                append(" from ").append(getEntityName()).append(" as ").append(getAliasName()).
                append(" ").append(joins).append(" ").append(qlCriteria);
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            if ((startPageNo < 1) || (pageSize < 1)) {
                return session.createQuery(sb.toString()).list();
            } else {
                return session.createQuery(sb.toString()).setFirstResult((startPageNo - 1)
                        * pageSize).setMaxResults(pageSize).list();
            }
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    /**
     * Return entities by execute Query.<br/>
     * Whether result is cacheable by your envoirment.<br/>
     * 執行Query以取得List&lt;E&gt;<br/>
     * 回傳結果是否Cacheable由整體環境設定決定
     * @param query
     * @return
     */
    protected List<Member> findList(Query query) {
        return query.list();
    }

    protected List<Member> findList(Query query, Object... values) {
        if (values != null && values.length > 0) {
            for (int i = 0; i < values.length; i++) {
                query.setParameter(i, values[i]);
            }
        }
        return query.list();
    }

    protected List<Member> findList(Query query, Map<String, ?> parameters) {
        if (parameters != null && !parameters.isEmpty()) {
            for (String key : parameters.keySet()) {
                query.setParameter(key, parameters.get(key));
            }
        }
        return query.list();
    }

    /**
     * Return a new {@link SQLQuery} by a sql statement.<br/>
     * 以SQL Statement 取得{@link SQLQuery 查詢}物件，執行時最好透過{@link #executeList(org.hibernate.Query)  executeList(Query)}或是
     * {@link #executeList(org.hibernate.Query) executeList(Query)執行以取得想要的結果}
     * @param sql
     * @return
     */
    protected SQLQuery getSQLQuery(String sql) {
        return getSQLQuery(sql, true);
    }

    protected SQLQuery getSQLQuery(String sql, boolean readOnly) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            SQLQuery qry = session.createSQLQuery(sql);
            qry.setReadOnly(readOnly);
            qry.setCacheable(false);
            return qry;
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findBySQLQuery(String sql) {
        return findList(getSQLQuery(sql).addEntity(getClazz()));
    }

    @Override
    public List<Member> findBySQLQuery(String sql, Object... parameters) {
        return findList(getSQLQuery(sql).addEntity(getClazz()), parameters);
    }

    @Override
    public List<Member> findBySQLQuery(String sql, Map<String, ?> parameters) {
        return findList(getSQLQuery(sql).addEntity(getClazz()), parameters);
    }

    @Override
    public List<Member> findBySQLQuery(String sql, String entityAlias) {
        return findList(getSQLQuery(sql).addEntity(entityAlias, getClazz()));
    }

    @Override
    public List<Member> findBySQLQuery(String sql, String entityAlias, Object... parameters) {
        return findList(getSQLQuery(sql).addEntity(entityAlias, getClazz()), parameters);
    }

    @Override
    public List<Member> findBySQLQuery(String sql, String entityAlias, Map<String, ?> parameters) {
        return findList(getSQLQuery(sql).addEntity(entityAlias, getClazz()), parameters);
    }

    @Override
    public <T> T findUniqueByQL(Class<T> clazz, String QL) {
        return findUniqueByQL(clazz, QL, (Object[]) null);
    }

    @Override
    public <T> T findUniqueByQL(Class<T> clazz, String QL, Object... parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
            if (parameters != null && parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    query.setParameter(i, parameters[i]);
                }
            }
            return (T) query.uniqueResult();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public <T> T findUniqueByQL(Class<T> clazz, String QL, Map<String, ?> parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
            if (parameters != null && !parameters.isEmpty()) {
                for (String key : parameters.keySet()) {
                    query.setParameter(key, parameters.get(key));
                }
            }
            return (T) query.uniqueResult();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public <T> List<T> findListByQL(Class<T> clazz, String QL) {
        return findListByQL(clazz, QL, (Object[]) null);
    }

    @Override
    public <T> List<T> findListByQL(Class<T> clazz, String QL, Object... parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        assert session != null : "****** sesssion is null ******";
        try {
            Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
            if (parameters != null && parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    query.setParameter(i, parameters[i]);
                }
            }
            return query.list();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public <T> List<T> findListByQL(Class<T> clazz, String QL, Map<String, ?> parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            Query query = session.createQuery(QL).setCacheable(false).setReadOnly(true);
            if (parameters != null && !parameters.isEmpty()) {
                for (String key : parameters.keySet()) {
                    query.setParameter(key, parameters.get(key));
                }
            }
            return query.list();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByNamedQuery(String name) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return session.getNamedQuery(name).list();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByNamedQuery(String name, Object... parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return findList(session.getNamedQuery(name), parameters);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public List<Member> findByNamedQuery(String name, Map<String, ?> parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return findList(session.getNamedQuery(name), parameters);
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public <T> List<T> findListByNamedQuery(Class<T> clazz, String name) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            return (List<T>) session.getNamedQuery(name).list();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public <T> List<T> findListByNamedQuery(Class<T> clazz, String name, Object... parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            Query query = session.getNamedQuery(name);
            if (parameters != null && parameters.length > 0) {
                for (int i = 0; i < parameters.length; i++) {
                    query.setParameter(i, parameters[i]);
                }
            }
            return (List<T>) query.list();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public <T> List<T> findListByNamedQuery(Class<T> clazz, String name, Map<String, ?> parameters) {
        Session session = SessionFactoryUtils.openSession(sessionFactory);
        try {
            Query query = session.getNamedQuery(name);
            if (parameters != null && !parameters.isEmpty()) {
                for (String key : parameters.keySet()) {
                    query.setParameter(key, parameters.get(key));
                }
            }
            return query.list();
        } finally {
            SessionFactoryUtils.closeSession(session);
        }
    }

    @Override
    public Member initLazyCollection(final Member entity, String collectionFieldName) {
        final AtomicBoolean found = new AtomicBoolean(false);
        final String methodName = collectionFieldName.matches("^[a-z][A-Z]") ? collectionFieldName : collectionFieldName.length() > 1
                ? collectionFieldName.substring(0, 1).toUpperCase() + collectionFieldName.substring(1) : collectionFieldName.toUpperCase();
        ReflectionUtils.doWithMethods(getClazz(),
                new ReflectionUtils.MethodCallback() {

                    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                        try {
                            Method setter = entity.getClass().getMethod("s" + method.getName().substring(1), method.getReturnType());
                            Object fieldObj = method.invoke(entity, new Object[]{});
                            if (fieldObj instanceof AbstractPersistentCollection) {
                                AbstractPersistentCollection ps = (AbstractPersistentCollection) fieldObj;
                                if (!ps.wasInitialized() && ps.getSession() == null) {
                                    Session session = SessionFactoryUtils.openSession(sessionFactory);
                                    try {
                                        session.update(entity);
                                        Hibernate.initialize(ps);
                                        setter.invoke(entity, ps);
                                    } finally {
                                        SessionFactoryUtils.closeSession(session);
                                    }
                                }
                            }
                        } catch (NoSuchMethodException ex) {
                            throw new HibernateException("Setter " + getClazz().getSimpleName() + ".set" + methodName + "(...) not found.", ex);
                        } catch (InvocationTargetException ex) {
                            throw new HibernateException("Could not fetch Collection from " + getClazz().getSimpleName() + "." + method.getName(), ex);
                        }
                    }
                },
                new ReflectionUtils.MethodFilter() {

                    public boolean matches(Method method) {
                        if (found.get()) {
                            return false;
                        } else {
                            found.set(method.getName().equals("get" + methodName) && method.getParameterTypes().length == 0
                                    && ClassUtils.isAssignable(Collection.class, method.getReturnType()));
                            return found.get();
                        }
                    }
                });
        if (!found.get()) {
            throw new HibernateException("Getter " + getClazz().getSimpleName() + ".get" + methodName + "() not found.");
        }
        return (Member) entity;
    }
}
